package com.punch.course.asyn;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.punch.common.enums.course.ClassStatusEnum;
import com.punch.common.exception.BusinessException;
import com.punch.common.feign.FeignHolidayService;
import com.punch.common.feign.FeignOrderService;
import com.punch.common.vo.order.StudentCourseClassVO;
import com.punch.course.exception.CourseExceptionEnum;
import com.punch.course.mapper.TimeTaskMapper;
import com.punch.course.model.*;
import com.punch.course.service.*;
import com.punch.course.util.CourseClassUtil;
import com.punch.course.util.CronUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 课程异步任务
 */
@Slf4j
@Component
@Async
public class CourseAsync {

    @Resource
    private ClassScheduleService classScheduleService;

    @Resource
    private FeignHolidayService feignHolidayService;

    @Resource
    private StudentCourseScheduleService studentCourseScheduleService;

    @Resource
    private FeignOrderService feignOrderService;

    @Resource
    private TimeTaskMapper timeTaskMapper;

    @Autowired
    @Lazy
    private TimeTaskService timeTaskService;

    @Autowired
    @Lazy
    private CourseClassService courseClassService;

    @Resource
    private CourseSchoolService courseSchoolService;


    /**
     * 班级发布生成开始报名定时任务
     *
     * @param classId         班级id
     * @param signUpStartTime 报名开始时间
     */
    @Transactional(rollbackFor = RuntimeException.class)
    public void toSignUpTimeTask(Long classId, LocalDateTime signUpStartTime) {
        QueryWrapper<TimeTask> queryWrapper = new QueryWrapper<>();
        //开始报名
        queryWrapper.lambda()
                .eq(TimeTask::getType, 0)
                .eq(TimeTask::getAssociateId, classId)
                .eq(TimeTask::getAssociateType, 0);
        TimeTask timeTaskEX = timeTaskMapper.selectOne(queryWrapper);
        //没有则新增
        if (timeTaskEX == null) {
            String timeCron = CronUtil.formatDateByPattern(signUpStartTime);
            TimeTask timeTask = new TimeTask();
            timeTask.setType(0);
            timeTask.setTimeCron(timeCron);
            timeTask.setAssociateId(classId);
            timeTask.setAssociateType(0);//班级开始报名
            timeTaskMapper.insert(timeTask);
            timeTaskService.addCron(timeTask.getId(), 0, classId, 0, timeCron);
        } else {
            Long timeTaskId = timeTaskEX.getId();
            String timeCron = CronUtil.formatDateByPattern(signUpStartTime);
            TimeTask timeTaskUpdate = new TimeTask();
            timeTaskUpdate.setTimeCron(timeCron);
            timeTaskUpdate.setId(timeTaskId);
            timeTaskMapper.updateById(timeTaskUpdate);
            timeTaskService.stopCron(timeTaskId);
            timeTaskService.updateCron(timeTaskId, 0, classId, 0, timeCron);
        }
    }


    /**
     * 开始报名定时任务,生成报名结束定时任务
     *
     * @param timeTaskId 定时任务id
     * @param classId    班级id
     */
    @Transactional(rollbackFor = Exception.class)
    public void toSignUp(Long timeTaskId, Long classId) {
        //删除原来的定时任务
        timeTaskMapper.deleteById(timeTaskId);
        log.info("开始报名:{}", classId);
        updateCourseClass(classId, ClassStatusEnum.SIGN_IN.getCode());
        CourseClass courseClassEX = courseClassService.getById(classId);
        Optional.ofNullable(courseClassEX).orElseThrow(() -> {
            log.info("课程班级id:{},不存在", classId);
            return new BusinessException(CourseExceptionEnum.CLASS_DETAIL_FAIL_NULL);
        });
        toOpenClassTimeTask(classId, courseClassEX.getSignUpEndTime());
    }


    /**
     * 生成报名结束,待开班
     *
     * @param classId       课程班级id
     * @param signUpEndTime 报名截止时间
     */
    public void toOpenClassTimeTask(Long classId, LocalDateTime signUpEndTime) {
        QueryWrapper<TimeTask> queryWrapper = new QueryWrapper<>();
        //开班
        queryWrapper.lambda()
                .eq(TimeTask::getType, 0)
                .eq(TimeTask::getAssociateId, classId)
                .eq(TimeTask::getAssociateType, 1);
        TimeTask timeTaskEX = timeTaskMapper.selectOne(queryWrapper);
        String timeCron = CronUtil.formatDateByPattern(signUpEndTime);
        //没有则新增
        if (timeTaskEX == null) {
            TimeTask timeTask = new TimeTask();
            timeTask.setType(0);
            timeTask.setTimeCron(timeCron);
            timeTask.setAssociateId(classId);
            timeTask.setAssociateType(1);//生成报名结束,待开班
            timeTaskMapper.insert(timeTask);
            //生成开班的定时任务
            timeTaskService.addCron(timeTask.getId(), 0, classId, 1, timeCron);
        } else {
            TimeTask timeTaskUpdate = new TimeTask();
            timeTaskUpdate.setTimeCron(timeCron);
            timeTaskUpdate.setId(timeTaskEX.getId());
            timeTaskMapper.updateById(timeTaskUpdate);
            timeTaskService.stopCron(timeTaskEX.getId());
            timeTaskService.updateCron(timeTaskEX.getId(), 0, classId, 1, timeCron);
        }
    }

    /**
     * 报名结束,待开班
     *
     * @param timeTaskId  任务id
     * @param associateId 课程班级id
     */
    public void toOpenClass(Long timeTaskId, Long associateId) {
        //删除原来的定时任务
        timeTaskMapper.deleteById(timeTaskId);
        updateCourseClass(associateId, ClassStatusEnum.TO_OPEN_CLASS.getCode());
        CourseClass courseClassEX = courseClassService.getById(associateId);
        //没有达到开班最小人数,没有达到最小人数开班失败,退款给用户
        if (courseClassEX.getSignUpNumber() < courseClassEX.getStuNoMin()) {
            log.info("报名结束,未达到开班人数,班级开班失败:{}", associateId);
            updateCourseClass(associateId, ClassStatusEnum.OPEN_CLASS_FAIL.getCode());
            // TODO: 2020/12/8 退款
            return;
        }
        Optional.ofNullable(courseClassEX).orElseThrow(() -> {
            log.info("课程班级id:{},不存在", associateId);
            return new BusinessException(CourseExceptionEnum.CLASS_DETAIL_FAIL_NULL);
        });
        //真正的开班日期
        openClassTimeTask(associateId, courseClassEX.getActualOpenDate());
    }

    /**
     * 开班生成定时任务
     *
     * @param classId  班级id
     * @param openDate 开班日期
     */
    @Transactional(rollbackFor = Exception.class)
    public void openClassTimeTask(Long classId, LocalDate openDate) {
        //开班定时任务插入
        LocalTime localTime = LocalTime.of(0, 0, 0);
        String timeCron = CronUtil.formatDateByPattern(openDate.atTime(localTime));
        QueryWrapper<TimeTask> queryWrapper = new QueryWrapper<>();
        //开班
        queryWrapper.lambda()
                .eq(TimeTask::getType, 0)
                .eq(TimeTask::getAssociateId, classId)
                .eq(TimeTask::getAssociateType, 2);
        TimeTask timeTaskEX = timeTaskMapper.selectOne(queryWrapper);
        //没有则新增
        if (timeTaskEX == null) {
            TimeTask timeTask = new TimeTask();
            timeTask.setType(0);
            timeTask.setTimeCron(timeCron);
            timeTask.setAssociateId(classId);
            timeTask.setAssociateType(2);//班级开班
            timeTaskMapper.insert(timeTask);
            //生成开班的定时任务
            timeTaskService.addCron(timeTask.getId(), 0, classId, 2, timeCron);
        } else {
            TimeTask timeTaskUpdate = new TimeTask();
            timeTaskUpdate.setTimeCron(timeCron);
            timeTaskUpdate.setId(timeTaskEX.getId());
            timeTaskMapper.updateById(timeTaskUpdate);
            timeTaskService.stopCron(timeTaskEX.getId());
            timeTaskService.updateCron(timeTaskEX.getId(), 0, classId, 2, timeCron);
        }
    }

    /**
     * 开班
     * <p>
     * 1.班级状态由报名中修改成已开班
     * 2.计算该课程课表
     *
     * @param timeTaskId 定时任务id
     * @param classId    班级id
     */
    @Transactional(rollbackFor = Exception.class)
    public void openClass(Long timeTaskId, Long classId) {
        //删除原来的定时任务
        timeTaskMapper.deleteById(timeTaskId);
        log.info("开班:{}", classId);
        CourseClass courseClassEX = courseClassService.getById(classId);
        Optional.ofNullable(courseClassEX).orElseThrow(() -> {
            log.info("课程班级id:{},不存在", classId);
            return new BusinessException(CourseExceptionEnum.CLASS_DETAIL_FAIL_NULL);
        });
        updateCourseClass(classId, ClassStatusEnum.OPEN_CLASS.getCode());
        //生成课程课表
        generateCourseClassSchedule(courseClassEX);
        //修改订单状态
        feignOrderService.startClass(classId);
    }


    /**
     * 异步修改课程数,定时任务
     *
     * @param classId 班级id
     */
    @Transactional(rollbackFor = Exception.class)
    public void removeTimeTaskAndUpdateCourse(Long classId, Integer courseHourCount) {
        //课程课时数要取class_schedule表的长度
        CourseSchool courseSchool = new CourseSchool();
        courseSchool.setCourseHourCount(courseHourCount);
        UpdateWrapper<CourseSchool> updateWrapper = new UpdateWrapper<>();
        updateWrapper.lambda().eq(CourseSchool::getClassId, classId);
        courseSchoolService.update(courseSchool, updateWrapper);

        QueryWrapper<TimeTask> taskQueryWrapper = new QueryWrapper<>();
        taskQueryWrapper.lambda().eq(TimeTask::getAssociateId, classId).eq(TimeTask::getType, 0);
        List<TimeTask> timeTasks = timeTaskMapper.selectList(taskQueryWrapper);
        timeTasks.forEach(timeTask -> {
            Long timeTaskId = timeTask.getId();
            timeTaskService.removeCron(timeTaskId);
            timeTaskMapper.deleteById(timeTaskId);
        });

    }

    /**
     * 更新课程信息
     *
     * @param classId 班级id
     * @param status  状态
     */
    private void updateCourseClass(Long classId, Integer status) {
        CourseClass courseClass = new CourseClass();
        courseClass.setId(classId);
        courseClass.setClassStatus(status);
        courseClassService.updateById(courseClass);
    }


    /**
     * 生成该课程班级的课表
     *
     * @param courseClassEX 课程信息
     */
    private void generateCourseClassSchedule(CourseClass courseClassEX) {
        Long classId = courseClassEX.getId();
        QueryWrapper<CourseSchool> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(CourseSchool::getClassId, classId);
        CourseSchool courseSchoolEX = courseSchoolService.getOne(queryWrapper);
        Long courseId = courseSchoolEX.getId();
        LocalDate planOpenDate = courseClassEX.getPlanOpenDate();
        Integer courseCount = courseSchoolEX.getCourseHourCount();
        String classTIme = courseClassEX.getClassTime();
        //删除原来的课表
        UpdateWrapper<ClassSchedule> updateWrapper = new UpdateWrapper<>();
        updateWrapper.lambda().eq(ClassSchedule::getCourseId, courseId).eq(ClassSchedule::getClassId, classId);
        classScheduleService.remove(updateWrapper);
        List<Integer> courseWeekList = CollUtil.newArrayList();
        String[] courseWeekSplit = courseSchoolEX.getCourseWeek().split(",");
        for (String courseWeek : courseWeekSplit) {
            courseWeekList.add(Integer.parseInt(courseWeek));
        }
        List<LocalDate> realOpenClassDateList = CourseClassUtil.getRealOpenClassDate(planOpenDate, courseWeekList, courseCount);
        //查找他们之间是否有节假日
        LocalDate startDate = realOpenClassDateList.get(0);
        LocalDate endDate = realOpenClassDateList.get(realOpenClassDateList.size() - 1);
        List<LocalDate> localDateList = feignHolidayService.listHoliday(startDate, endDate);
        String[] split = classTIme.split("~");
        LocalTime startTime = LocalTime.parse(split[0]);
        LocalTime endTime = LocalTime.parse(split[1]);
        List<ClassSchedule> list = new ArrayList<>();
        AtomicInteger count = new AtomicInteger();
        realOpenClassDateList.forEach(localDate -> {
            ClassSchedule classSchedule = new ClassSchedule();
            classSchedule.setCourseId(courseId);
            classSchedule.setClassId(classId);
            LocalDateTime courseDate = LocalDateTime.of(localDate, startTime);
            classSchedule.setCourseStartDate(courseDate);
            courseDate = LocalDateTime.of(localDate, endTime);
            classSchedule.setCourseEndDate(courseDate);
            //判断该上课日期是否是节假日
            if (localDateList.contains(localDate)) {
                classSchedule.setHolidayFlag(1);
            } else {
                count.getAndIncrement();
                classSchedule.setHolidayFlag(0);
            }
            list.add(classSchedule);
        });
        classScheduleService.saveBatch(list);
        //每个学生生成课表
        generateStudentCourseSchedule(courseClassEX, courseSchoolEX.getCourseName(), list);
    }

    /**
     * 为该课程的每个学生异步插入课表
     *
     * @param courseClassEX     班级信息
     * @param courseName        课程名称
     * @param classScheduleList 开课计划列表
     */
    private void generateStudentCourseSchedule(CourseClass courseClassEX, String courseName, List<ClassSchedule> classScheduleList) {
        Long classId = courseClassEX.getId();
        List<StudentCourseClassVO> stuIdList = feignOrderService.getCourseClassOrderStudent(classId);
        if (CollUtil.isEmpty(stuIdList)) {
            throw new BusinessException("异步任务获取该开班课程的学生列表失败");
        }
        //删除原来的学生课表, 因为有业务是不断添加学生情况
        QueryWrapper<StudentCourseSchedule> studentCourseScheduleQueryWrapper = new QueryWrapper<>();
        studentCourseScheduleQueryWrapper.lambda().eq(StudentCourseSchedule::getClassId, classId);
        studentCourseScheduleService.remove(studentCourseScheduleQueryWrapper);
        //查找该课程开班的课表

        List<StudentCourseSchedule> insertList = new ArrayList<>();
        stuIdList.forEach(studentCourseClassVO -> classScheduleList.forEach(classSchedule -> {
            StudentCourseSchedule studentCourseSchedule = new StudentCourseSchedule();
            studentCourseSchedule.setCourseId(courseClassEX.getCourseId());
            studentCourseSchedule.setCourseName(courseName);
            studentCourseSchedule.setClassId(classId);
            studentCourseSchedule.setTeacherId(courseClassEX.getTeacherId());
            studentCourseSchedule.setSchoolId(courseClassEX.getSchoolId());
            studentCourseSchedule.setSchoolName(courseClassEX.getSchoolName());
            studentCourseSchedule.setClassRoomName(courseClassEX.getClassRoomName());
            studentCourseSchedule.setClassDate(classSchedule.getCourseStartDate().toLocalDate());
            studentCourseSchedule.setClassTime(courseClassEX.getClassTime());
            //学生信息
            studentCourseSchedule.setStudentId(studentCourseClassVO.getStudentId());
            studentCourseSchedule.setStudentName(studentCourseClassVO.getStudentName());
            studentCourseSchedule.setStudentSex(studentCourseClassVO.getStudentSex());
            studentCourseSchedule.setStudentGrade(studentCourseClassVO.getStudentGrade());
            studentCourseSchedule.setStudentClass(studentCourseClassVO.getStudentClass());
            studentCourseSchedule.setGuardianMobile(studentCourseClassVO.getGuardianMobile());
            insertList.add(studentCourseSchedule);
        }));
        studentCourseScheduleService.saveBatch(insertList);
    }
}
